﻿using FFmpeg.AutoGen;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FFmpegWinui3.MediaFolder
{
    public unsafe class MediaContainer
    {

        private Clock clock = new Clock();
        private readonly object SyncLock = new object();

        public string Url { get; private set; }

        public AVFormatContext* format = null;
        public AVPacket* packet = null;

        public TimeSpan Duration { get; protected set; }

        public Dictionary<AVMediaType, MediaBase> Items { get; set; } = new Dictionary<AVMediaType, MediaBase>();
        public VideoMedia Video
        {
            get
            {
                return Items.ContainsKey(AVMediaType.AVMEDIA_TYPE_VIDEO) ? Items[AVMediaType.AVMEDIA_TYPE_VIDEO] as VideoMedia : null;
            }
        }
        public AudioMedia Audio { get => Items.ContainsKey(AVMediaType.AVMEDIA_TYPE_AUDIO) ? Items[AVMediaType.AVMEDIA_TYPE_AUDIO] as AudioMedia : null; }

        public VideoMedia VideoMedia { get; protected set; }
        public bool IsRuning { get { lock (SyncLock) { return clock.IsRuning; } } }
        public TimeSpan Position { get => clock.Position; private set { clock.Position = value; } }
        public MediaContainer()
        {

        }

        /// <summary>
        /// 初始化媒体
        /// </summary>
        public void InitMedia(string url)
        {
            Url = url;
            format = ffmpeg.avformat_alloc_context();
            var temp = format;
            ffmpeg.avformat_open_input(&temp, url, null, null);
            ffmpeg.avformat_find_stream_info(format, null);
            packet = ffmpeg.av_packet_alloc();
            for (int i = 0; i < format->nb_streams; i++)
            {
                var type = format->streams[i]->codecpar->codec_type;
                switch (type)
                {
                    case AVMediaType.AVMEDIA_TYPE_VIDEO:
                        Items[type] = new VideoMedia(format, i);
                        break;
                    case AVMediaType.AVMEDIA_TYPE_AUDIO:
                        Items[type] = new AudioMedia(format, i);
                        break;
                }
            }
            Duration = TimeSpan.FromMilliseconds(format->duration / 1000);
        }
        /// <summary>
        /// 读取下一帧数据包，并存储到对应的模块中
        /// </summary>
        public void ReadNextPacket()
        {
            lock (SyncLock)
            {
                if (format == null)
                    return;

                if (Video == null && Audio == null)
                    return;
                while (true)
                {
                    if (Video?.FrameQuene?.Count > 20)
                        return;
                    ffmpeg.av_packet_unref(packet);
                    var result = ffmpeg.av_read_frame(format, packet);
                    if (result < 0)
                        return;
                    foreach (var item in Items)
                    {
                        if (item.Value.StreamIndex == packet->stream_index)
                        {
                            ffmpeg.avcodec_send_packet(item.Value.CodecContext, packet);
                            var frame = ffmpeg.av_frame_alloc();
                            var reslut = ffmpeg.avcodec_receive_frame(item.Value.CodecContext, frame);
                            if (reslut < 0)
                                break;
                            item.Value.AddFrame(frame, Position);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 获取视频帧信息
        /// </summary>
        /// <returns></returns>
        public byte[] GetVideoFrameData()
        {
            lock (SyncLock)
            {
                if (Video == null)
                    return null;
                //Debug.WriteLine(clock.Position.ToString());
                return Video.GetNextFrameData(Position);
            }
        }
        public byte[] GetAudioFrameData()
        {
            lock (SyncLock)
            {
                if (Audio == null)
                    return null;
                return Audio.GetNextFrameData(Position);
            }
        }
        public void Play()
        {
            lock (SyncLock)
            {
                clock.Play();
            }
        }
        public void Pause()
        {
            lock (SyncLock)
            {
                clock.Pause();
            }
        }
        public void Stop()
        {
            lock (SyncLock)
            {
                clock.Reset();
            }
        }

        public void Seek(int seekTime)
        {
            lock (SyncLock)
            {
                clock.Pause();
                Position = TimeSpan.FromSeconds(seekTime);

                if (Video != null)
                {
                    Video.ClearFrame();
                    var timestamp = seekTime / ffmpeg.av_q2d(Video.Stream->time_base);
                    ffmpeg.av_seek_frame(format, Video.StreamIndex, (long)timestamp, ffmpeg.AVSEEK_FLAG_BACKWARD | ffmpeg.AVSEEK_FLAG_FRAME);
                }

                if (Audio != null)
                {
                    Audio.ClearFrame();
                    var timestamp = seekTime / ffmpeg.av_q2d(Audio.Stream->time_base);
                    ffmpeg.av_seek_frame(format, Audio.StreamIndex, (long)timestamp, ffmpeg.AVSEEK_FLAG_BACKWARD | ffmpeg.AVSEEK_FLAG_FRAME);
                }

                while (true)
                {
                    if (Video?.FrameQuene?.Count > 20)
                    {
                        if (Video != null)
                            Video.ClearFrame();


                        if (Audio != null)
                            Audio.ClearFrame();

                        clock.Play();
                        return;
                    }
                    ffmpeg.av_packet_unref(packet);
                    var result = ffmpeg.av_read_frame(format, packet);
                    if (result < 0)
                    {
                        if (Video != null)
                            Video.ClearFrame();


                        if (Audio != null)
                            Audio.ClearFrame();
                        clock.Play();
                        return;
                    }

                    foreach (var item in Items)
                    {
                        if (item.Value.StreamIndex == packet->stream_index)
                        {
                            ffmpeg.avcodec_send_packet(item.Value.CodecContext, packet);
                            var frame = ffmpeg.av_frame_alloc();
                            var reslut = ffmpeg.avcodec_receive_frame(item.Value.CodecContext, frame);
                            if (reslut < 0)
                                break;
                            item.Value.AddFrame(frame, Position);
                            break;
                        }
                    }
                }

            }
        }
    }
}
